/**
  ******************************************************************************
  * @file    Menu_VCOM.c
  * @author  Milandr Application Team
  * @version V1.2.0
  * @date    12.09.2011
  * @brief   This file contains all the "USB VCOM" menu handlers.
  ******************************************************************************
  * <br><br>
  * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS
  * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE
  * TIME. AS A RESULT, MILANDR SHALL NOT BE HELD LIABLE FOR ANY
  * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING
  * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE
  * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS.
  *
  * <h2><center>&copy; COPYRIGHT 2021 Milandr</center></h2>
  */
/* Includes ------------------------------------------------------------------*/
#include "MDR32F9Qx_config.h"
#include <MDR32Fx.h>
#include <MDR32F9Qx_port.h>
#include <MDR32F9Qx_rst_clk.h>
#include <MDR32F9Qx_uart.h>
#include <MDR32F9Qx_usb_handlers.h>
#include "systick.h"
#include "Menu.h"
#include "Menu_items.h"
#include "leds.h"
#include "lcd.h"
#include "text.h"
#include "joystick.h"
#include "demo_init.h"
#include "MDR32F9Qx_it.h"

/** @addtogroup __MDR32F9Qx_Eval_Demo MDR32F9Qx Demonstration Example
  * @{
  */

/** @addtogroup Menu Menu
  * @{
  */

/** @addtogroup Menu_USB Menu USB VCOM
  * @{
  */

/** @defgroup Menu_USB_Private_Constants Menu USB VCOM Private Constants
  * @{
  */

#define BUFFER_LENGTH                        100

#if defined (USE_MDR32F9Q1_Rev0) || defined (USE_MDR32F9Q1_Rev1)
#define UART                                 MDR_UART2
#define RST_CLK_PCLK_UART                    RST_CLK_PCLK_UART2
#define UART_IRQn                            UART2_IRQn
#else
#define UART                                 MDR_UART1
#define RST_CLK_PCLK_UART                    RST_CLK_PCLK_UART1
#define UART_IRQn                            UART1_IRQn
#endif /* (USE_MDR32F9Q1_Rev0) || defined (USE_MDR32F9Q1_Rev1) */

/** @} */ /* End of group Menu_USB_Private_Constants */

/** @defgroup Menu_USB_Private_Types Menu USB VCOM Private Types
  * @{
  */

/** @} */ /* End of group Menu_USB_Private_Types */

/** @defgroup Menu_USB_Private_Macros Menu USB VCOM Private Macros
  * @{
  */

/** @} */ /* End of group Menu_USB_Private_Macros */

/** @defgroup Menu_USB_Private_Variables Menu USB VCOM Private Variables
  * @{
  */

static uint8_t SendBuffer[BUFFER_LENGTH];
static uint8_t ReceiveBuffer[BUFFER_LENGTH];

static vuint32_t send_data_length = 0;
static vuint32_t send_data_pos = 0;
static vuint32_t receive_data_length = 0;

USB_Clock_TypeDef USB_Clock_InitStruct;
USB_DeviceBUSParam_TypeDef USB_DeviceBUSParam;

static UART_InitTypeDef UARTInitStructure;

static uint32_t temp_CPU_CLOCK, temp_PLL_CONTROL;

#ifdef USB_CDC_LINE_CODING_SUPPORTED
static USB_CDC_LineCoding_TypeDef LineCoding;
#endif /* USB_CDC_LINE_CODING_SUPPORTED */

#if defined (USE_MDR32F9Q2_Rev0) || defined (USE_MDR32F9Q2_Rev1)
static MDR_PORT_TypeDef PORTB_Configuration;
#endif /* defined (USE_MDR32F9Q2_Rev0) || defined (USE_MDR32F9Q2_Rev1) */

/** @} */ /* End of group Menu_USB_Private_Variables */

/** @defgroup Menu_USB_Private_Functions Menu USB VCOM Private Functions
  * @{
  */

/* Private function prototypes -----------------------------------------------*/
static void Setup_CPU_Clock(void);
static void Setup_USB(void);
static void UARTConfiguration(void);
static void SenderFunc(void);
static void ReceiverFunc(void);
#ifdef USB_CDC_STATE_REPORTING_SUPPORTED
static void LineStateFunc(uint32_t events);
#endif /* USB_CDC_STATE_REPORTING_SUPPORTED */

/* Private functions ---------------------------------------------------------*/

/*******************************************************************************
* Function Name  : Setup_CPU_Clock
* Description    : Frequencies setup
* Output         : None
* Return         : USB_Result
*******************************************************************************/
static void Setup_CPU_Clock(void)
{
  /* Enable HSE */
  RST_CLK_HSEconfig(RST_CLK_HSE_ON);
  if (RST_CLK_HSEstatus() != SUCCESS)
  {
    /* Trap */
    while (1)
    {
    }
  }

  /* CPU_C1_SEL = HSE */
  RST_CLK_CPU_PLLconfig(RST_CLK_CPU_PLLsrcHSEdiv1, RST_CLK_CPU_PLLmul10);
  RST_CLK_CPU_PLLcmd(ENABLE);
  if (RST_CLK_CPU_PLLstatus() != SUCCESS)
  {
    /* Trap */
    while (1)
    {
    }
  }

  /* CPU_C3_SEL = CPU_C2_SEL */
  RST_CLK_CPUclkPrescaler(RST_CLK_CPUclkDIV1);
  /* CPU_C2_SEL = PLL */
  RST_CLK_CPU_PLLuse(ENABLE);
  /* HCLK_SEL = CPU_C3_SEL */
  RST_CLK_CPUclkSelection(RST_CLK_CPUclkCPU_C3);
}

/*******************************************************************************
* Function Name  : Setup_USB
* Description    : USB Device layer setup and powering on
* Output         : None
* Return         : USB_Result
*******************************************************************************/
static void Setup_USB(void)
{
  /* Enables the CPU_CLK clock on USB */
  RST_CLK_PCLKcmd(RST_CLK_PCLK_USB, ENABLE);

  /* Device layer initialization */
  USB_Clock_InitStruct.USB_USBC1_Source = USB_C1HSEdiv2;
  USB_Clock_InitStruct.USB_PLLUSBMUL    = USB_PLLUSBMUL12;

  USB_DeviceBUSParam.MODE  = USB_SC_SCFSP_Full;
  USB_DeviceBUSParam.SPEED = USB_SC_SCFSR_12Mb;
  USB_DeviceBUSParam.PULL  = USB_HSCR_DP_PULLUP_Set;

  USB_DeviceInit(&USB_Clock_InitStruct, &USB_DeviceBUSParam);

  /* Enable all USB interrupts */
  USB_SetSIM(USB_SIS_Msk);

  USB_DevicePowerOn();

  /* Enable interrupt on USB */
#ifdef USB_INT_HANDLE_REQUIRED
  NVIC_EnableIRQ(USB_IRQn);
#endif /* USB_INT_HANDLE_REQUIRED */

  USB_DEVICE_HANDLE_RESET;
}


/*******************************************************************************
* Function Name  : USB_CDC_RecieveData
* Description    : CDC data reception handler.
*                : Transfers received data to UART
* Output         : None
* Return         : USB_Result
*******************************************************************************/
USB_Result USB_CDC_RecieveData(uint8_t* Buffer, uint32_t Length)
{
  /* Initiate UART transmission */
  send_data_length = Length;
  send_data_pos = 0;
  
  while(UART_GetFlagStatus(UART, UART_FLAG_TXFF) == SET);
  
  SenderFunc();

  return USB_SUCCESS;
}


#ifdef USB_CDC_LINE_CODING_SUPPORTED

/*******************************************************************************
* Function Name  : USB_CDC_GetLineCoding
* Description    : GetLineCoding CDC request handler.
*                : Sends back current line coding settings.
* Output         : None
* Return         : USB_Result
*******************************************************************************/
USB_Result USB_CDC_GetLineCoding(uint16_t wINDEX, USB_CDC_LineCoding_TypeDef* DATA)
{
  assert_param(DATA);
  if (wINDEX != 0)
  {
    /* Invalid interface */
    return USB_ERR_INV_REQ;
  }

  /* Just send back settings stored earlier */
  *DATA = LineCoding;

  return USB_SUCCESS;
}


/*******************************************************************************
* Function Name  : USB_CDC_SetLineCoding
* Description    : SetLineCoding CDC request handler.
*                : Stores received line coding settings and adjusts UART.
* Output         : None
* Return         : USB_Result
*******************************************************************************/
USB_Result USB_CDC_SetLineCoding(uint16_t wINDEX, const USB_CDC_LineCoding_TypeDef* DATA)
{
  assert_param(DATA);
  if (wINDEX != 0)
  {
    /* Invalid interface */
    return USB_ERR_INV_REQ;
  }

  /* Adjust UART settings */

  /* Baud rate */
  UARTInitStructure.UART_BaudRate = DATA->dwDTERate;

  /* Stop bits */
  switch (DATA->bCharFormat)
  {
    case USB_CDC_STOP_BITS1:
      UARTInitStructure.UART_StopBits = UART_StopBits1;
      break;
    case USB_CDC_STOP_BITS2:
      UARTInitStructure.UART_StopBits = UART_StopBits2;
      break;
    default :
      return USB_ERR_INV_REQ;
  }

  /* Parity */
  switch (DATA->bParityType)
  {
    case USB_CDC_PARITY_NONE:
      UARTInitStructure.UART_Parity = UART_Parity_No;
      break;
    case USB_CDC_PARITY_ODD:
      UARTInitStructure.UART_Parity = UART_Parity_Odd;
      break;
    case USB_CDC_PARITY_EVEN:
      UARTInitStructure.UART_Parity = UART_Parity_Even;
      break;
    case USB_CDC_PARITY_MARK:
      UARTInitStructure.UART_Parity = UART_Parity_0;
      break;
    case USB_CDC_PARITY_SPACE:
      UARTInitStructure.UART_Parity = UART_Parity_1;
      break;
    default :
      return USB_ERR_INV_REQ;
  }

  /* Data bits */
  switch (DATA->bDataBits)
  {
    case USB_CDC_DATA_BITS5:
      UARTInitStructure.UART_WordLength = UART_WordLength5b;
      break;
    case USB_CDC_DATA_BITS6:
      UARTInitStructure.UART_WordLength = UART_WordLength6b;
      break;
    case USB_CDC_DATA_BITS7:
      UARTInitStructure.UART_WordLength = UART_WordLength7b;
      break;
    case USB_CDC_DATA_BITS8:
      UARTInitStructure.UART_WordLength = UART_WordLength8b;
      break;
    default :
      return USB_ERR_INV_REQ;
  }

  /* Re-initialize UART */
  UART_Cmd(UART, DISABLE);
  UART_DeInit(UART);
  UART_Init(UART, &UARTInitStructure);
  /* Enable sender, receiver and line state interrupts */
  UART_ITConfig (UART, UART_IT_TX | UART_IT_RX
#ifdef USB_CDC_STATE_REPORTING_SUPPORTED
              | UART_LINE_STATE_EVENTS
#endif /* USB_CDC_STATE_REPORTING_SUPPORTED */
              , ENABLE);
  UART_Cmd(UART, ENABLE);

  /* On success, store new values for GetLineCoding request fastening */
  LineCoding = *DATA;

  return USB_SUCCESS;
}

#endif /* USB_CDC_LINE_CODING_SUPPORTED */

#if defined (USE_MDR32F9Q2_Rev0) || defined (USE_MDR32F9Q2_Rev1)
/*******************************************************************************
* Function Name  : PORTBSaveConfig
* Description    : Save the current PORTB configuration.
* Output         : None
* Return         : None
*******************************************************************************/
void PORTBSaveConfig(void)
{
  PORTB_Configuration.ANALOG = MDR_PORTB->ANALOG;
  PORTB_Configuration.FUNC   = MDR_PORTB->FUNC;
  PORTB_Configuration.GFEN   = MDR_PORTB->GFEN;
  PORTB_Configuration.OE     = MDR_PORTB->OE;
  PORTB_Configuration.PD     = MDR_PORTB->PD;
  PORTB_Configuration.PULL   = MDR_PORTB->PULL;
  PORTB_Configuration.PWR    = MDR_PORTB->PWR;
}

/*******************************************************************************
* Function Name  : PORTBRestoreConfig
* Description    : Restore saved PORTB configuration.
* Output         : None
* Return         : None
*******************************************************************************/
void PORTBRestoreConfig(void)
{
  MDR_PORTB->ANALOG = PORTB_Configuration.ANALOG;
  MDR_PORTB->FUNC   = PORTB_Configuration.FUNC;
  MDR_PORTB->GFEN   = PORTB_Configuration.GFEN;
  MDR_PORTB->OE     = PORTB_Configuration.OE;
  MDR_PORTB->PD     = PORTB_Configuration.PD;
  MDR_PORTB->PULL   = PORTB_Configuration.PULL;
  MDR_PORTB->PWR    = PORTB_Configuration.PWR;
}
#endif /* defined (USE_MDR32F9Q2_Rev0) || defined (USE_MDR32F9Q2_Rev1) */


/*******************************************************************************
* Function Name  : UARTConfiguration
* Description    : Configures the UART.
*                : Configures the HCLK division factor and RTCHSE clock for UART.
* Output         : None
* Return         : None
*******************************************************************************/
static void UARTConfiguration(void)
{
  /* Enable the RTCHSE clock on UART */
  RST_CLK_PCLKcmd(RST_CLK_PCLK_UART, ENABLE);

  /* Set the UART HCLK division factor */
  UART_BRGInit(UART, UART_HCLKdiv16);

  UART_DeInit(UART);

#if defined (USE_MDR32F9Q1_Rev0) || defined (USE_MDR32F9Q1_Rev1)
  /* Configure PORTF pins for data transfer to/from UART */
  PortInitStructure.PORT_Pin   = PORT_Pin_0 | PORT_Pin_1;
  PortInitStructure.PORT_MODE  = PORT_MODE_DIGITAL;
  PortInitStructure.PORT_FUNC  = PORT_FUNC_OVERRID;
  PortInitStructure.PORT_SPEED = PORT_SPEED_MAXFAST;
  PORT_Init(MDR_PORTF, &PortInitStructure);
#else
#if defined (USE_MDR32F9Q2_Rev0) && !defined (USE_MDR32F9Q2_Rev1)
  PORTBSaveConfig();
#endif /* defined (USE_MDR32F9Q2_Rev0) && !defined (USE_MDR32F9Q2_Rev1) */
  /* Configure PORTB pins for data transfer to/from UART */
  PortInitStructure.PORT_Pin   = PORT_Pin_5 | PORT_Pin_6;
  PortInitStructure.PORT_MODE  = PORT_MODE_DIGITAL;
  PortInitStructure.PORT_FUNC  = PORT_FUNC_ALTER;
  PortInitStructure.PORT_SPEED = PORT_SPEED_MAXFAST;
  PORT_Init(MDR_PORTB, &PortInitStructure);
#endif /* defined (USE_MDR32F9Q1_Rev0) || defined (USE_MDR32F9Q1_Rev1) */

  /* Enable interrupt on UART */
  NVIC_EnableIRQ(UART_IRQn);
}

/*******************************************************************************
* Function Name  : VCOMHelp
* Description    : Prints hint to the screen.
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
void VCOMHelp(void)
{
  sFONT *OldFont = CurrentFont;
  LCD_Method OldMethod = CurrentMethod;

  LCD_CLS();
  CurrentMethod = MET_AND;
  CurrentFont = &Font_6x8;
  DisplayMenuTitle("MDR_USB. Virtual COM");
  WAIT_UNTIL_KEY_RELEASED(SEL);
  LCD_PUTS(0, CurrentFont->Height * 2 + 2,     "  To stop Press SEL");

  CurrentMethod = OldMethod;
  CurrentFont = OldFont;
}

/*******************************************************************************
* Function Name  : SenderFunc
* Description    : Sender interrupt handler.
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
static void SenderFunc(void)
{
  for (; (send_data_pos < send_data_length && (UART_GetFlagStatus (UART, UART_FLAG_TXFF) == RESET)); send_data_pos++)
  {
    UART_SendData(UART, SendBuffer[send_data_pos]);
  }
}

/*******************************************************************************
* Function Name  : ReceiverFunc
* Description    : Receiver interrupt handler.
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
static void ReceiverFunc(void)
{
  uint16_t receive_data;

  /* Read data from UART */
  for (receive_data_length = 0; ((UART_GetFlagStatus(UART, UART_FLAG_RXFE) == RESET) && (receive_data_length < BUFFER_LENGTH)); )
  {
    receive_data = UART_ReceiveData(UART);

    if (UART_Flags(receive_data) == 0)
    {
      ReceiveBuffer[receive_data_length++] = UART_Data(receive_data);
    }
  }
  /* Initiate data portion sending via USB */
  USB_CDC_SendData(ReceiveBuffer, receive_data_length);
}

#ifdef USB_CDC_STATE_REPORTING_SUPPORTED
/*******************************************************************************
* Function Name  : LineStateFunc
* Description    : Line state event interrupt handler.
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
static void LineStateFunc(uint32_t events)
{
  uint16_t lineState = 0;
  if (events & UART_IT_OE)
  {
  lineState |= USB_CDC_bOverRun;
  }
  if (events & UART_IT_BE)
  {
  lineState |= USB_CDC_bBreak;
  }
  if (events & UART_IT_PE)
  {
  lineState |= USB_CDC_bParity;
  }
  if (events & UART_IT_FE)
  {
  lineState |= USB_CDC_bFraming;
  }
  if (events & UART_IT_DSR)
  {
  lineState |= USB_CDC_bTxCarrier;
  }
  if (events & UART_IT_DCD)
  {
  lineState |= USB_CDC_bRxCarrier;
  }
  if (events & UART_IT_RI)
  {
  lineState |= USB_CDC_bRingSignal;
  }

  if (lineState)
  {
    USB_CDC_ReportState(lineState);
  }
}
#endif /* USB_CDC_STATE_REPORTING_SUPPORTED */

/*******************************************************************************
* Function Name  : VCOMFunc
* Description    : Demonstration of USB-to-UART VCOM bridge.
*                : Displays prompt on LCD, enables the bridge and waits for
*                : joystick SEL key pressed to stop bridging and return
*                : to main menu.
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
void VCOMFunc(void) {
  uint32_t key;

  /* Display prompt */
  VCOMHelp();

  /* Save current CPU CLOCK configuration */
  temp_CPU_CLOCK   = MDR_RST_CLK->CPU_CLOCK;
  temp_PLL_CONTROL = MDR_RST_CLK->PLL_CONTROL;

  Setup_CPU_Clock();

  UARTConfiguration();

  /* CDC layer initialization */
  USB_CDC_Init(SendBuffer, 1, SET);

  /* UART configuration */
  UARTInitStructure.UART_BaudRate                = 14400;
  UARTInitStructure.UART_WordLength              = UART_WordLength8b;
  UARTInitStructure.UART_StopBits                = UART_StopBits1;
  UARTInitStructure.UART_Parity                  = UART_Parity_No;
  UARTInitStructure.UART_FIFOMode                = UART_FIFO_OFF;
  UARTInitStructure.UART_HardwareFlowControl     = UART_HardwareFlowControl_RXE | \
                                                   UART_HardwareFlowControl_TXE;
  /* With LBE bit set, output line of UARTTXD transmitter becomes to be bound to
   * UARTRXD receiver input. In order to run this example with UART physically
   * connected to PC, just comment this line */
/*  UARTInitStructure.UART_HardwareFlowControl     |= UART_HardwareFlowControl_LBE; */

  UART_Init (UART,&UARTInitStructure);

#ifdef USB_CDC_LINE_CODING_SUPPORTED
  /* Set line coding initial settings accordingly to UART ones above */
  LineCoding.dwDTERate = UARTInitStructure.UART_BaudRate;
  LineCoding.bCharFormat = USB_CDC_STOP_BITS1;
  LineCoding.bParityType = USB_CDC_PARITY_NONE;
  LineCoding.bDataBits = USB_CDC_DATA_BITS8;
#endif /* USB_CDC_LINE_CODING_SUPPORTED */

  /* Set interrupt handlers */
  pfUARTReceiverFunc = ReceiverFunc;
  pfUARTSenderFunc = SenderFunc;
#ifdef USB_CDC_STATE_REPORTING_SUPPORTED
  pfUARTLineStateFunc = LineStateFunc;
#endif /* USB_CDC_STATE_REPORTING_SUPPORTED */

  /* Enable sender, receiver and line state interrupts */
  UART_ITConfig (UART, UART_IT_TX | UART_IT_RX
#ifdef USB_CDC_STATE_REPORTING_SUPPORTED
              | UART_LINE_STATE_EVENTS
#endif /* USB_CDC_STATE_REPORTING_SUPPORTED */
              , ENABLE);

  /* Enable bridge */
  UART_Cmd(UART, ENABLE);
  Setup_USB();

  /* Wait for SEL to quit */
  key = GetKey();
  for (; key != SEL; key = GetKey())
  {
  }
  WAIT_UNTIL_KEY_RELEASED(SEL);

  /* Disable bridge */
  UART_Cmd(UART, DISABLE);
#if defined (USE_MDR32F9Q2_Rev0) || defined (USE_MDR32F9Q2_Rev1)
  PORTBRestoreConfig();
#endif /* defined (USE_MDR32F9Q2_Rev0) || defined (USE_MDR32F9Q2_Rev1) */
  USB_CDC_ReceiveStop();
  USB_DevicePowerOff();

  /* Restore original CPU CLOCK configuration */
  MDR_RST_CLK->CPU_CLOCK   = temp_CPU_CLOCK;
  MDR_RST_CLK->PLL_CONTROL = temp_PLL_CONTROL;

  DisplayMenu();
}


/** @} */ /* End of group Menu_USB_Private_Functions */

/** @} */ /* End of group Menu_USB */

/** @} */ /* End of group Menu */

/** @} */ /* End of group __MDR32F9Qx_Eval_Demo */

/******************* (C) COPYRIGHT 2021 Milandr *******************
*
* END OF FILE Menu_VCOM.c */


